import { Asset, CurveRange, JsonAsset } from "cc";
import { Engine } from "../../core/Engine";
import Logger from "../../core/utils/Logger";
import { getNowTime, getNumFloor, getPassTimeForSecond } from "../../core/utils/utils";
import { DATA_OPTION, DATA_PROPERTY, EVENT_NAME } from "../GameConstant";
import { gameRecordManager, PLAYER_STORAGE_KEY } from "../manager/GameRecordManager";
import { Dijkstra } from "../map/Dijkstra";
import { BaseData } from "./BaseData";
import { dataManager } from "./DataManager";
import { EnemyData } from "./EnemyData";
import { PlayerData } from "./PlayerData";
import { DispatchArmyInfo } from "./SoldierData";

/**
 * Predefined variables
 * Name = MapData
 * DateTime = Sun Dec 19 2021 00:07:38 GMT+0800 (中国标准时间)
 * Author = han2007happy
 * FileBasename = MapData.ts
 * FileBasenameNoExtension = MapData
 * URL = db://assets/scripts/game/data/MapData.ts
 * ManualUrl = https://docs.cocos.com/creator/3.3/manual/en/
 *
 */

export const MAP_CELL_SIZE = {
    tileWidth: 1024,
    tileHeight: 1024,
};

export const MAP_CELL_NUM = {
    tileRow: 3,
    tileColno: 6,
};

export enum TERRIAN_TYPE {
    BLANK = 1,
    MINE,
    ROCK,
    FARM,
    WATER,
    WASTE,
    SOUL,
}

export enum BUILD_TYPE {
    FARM = 1,
    IRON,
    FORG,
    FOREST,
    STONM,
    CRYSTAL,
    CEMETERY,
    ALTAR,
}

export enum BELONG_TYPE {
    MIDDLE_SIDE,
    SELF_SIDE,
    ENEMY_SIDE,
}

export interface CityInfoData {
    id: string;
    name: string;
    pos: string;
    type: number;
    res: string;
    des: string;
    requiredBuildTypes: number[];
    armyData: { [soldierId: string]: number };
    materialData: { [materialId: string]: number };
    belong: BELONG_TYPE;
}

export interface BuildInfoData {
    id: string;
    cityId: string;
    cfgId: string;
    name: string;
    type: number;
    res: string;
    des: string;
    outMaterials: { [materialId: string]: number };
}

export class MapData extends BaseData {
    // 配置数据
    bgBlockList: { [blockId: string]: string } = {};
    terrainList: { [cityId: string]: { type: number; pos: string } } = {};
    terrainLines: {
        [lineName: string]: { startPos: string; toPos: string; len: number };
    } = {};

    // 寻路算法
    dijks: Dijkstra | undefined;

    cityCfgDatas: {
        [cityId: string]: {
            [proKey: string]: any;
        };
    } = {};

    // 存储玩家数据
    cityList: {
        [cityId: string]: {
            soldiers: { [soldierId: string]: number };
            materials: { [materialId: string]: number };
            belongTo: BELONG_TYPE;
            lastStoreTime?: number;
        };
    } = {
        ["8"]: {
            soldiers: { ["soldier-1"]: 20 },
            materials: {},
            belongTo: BELONG_TYPE.SELF_SIDE,
        },
        ["25"]: {
            soldiers: { ["soldier-1"]: 100 },
            materials: {},
            belongTo: BELONG_TYPE.ENEMY_SIDE,
        },
        ["39"]: {
            soldiers: { ["soldier-1"]: 100 },
            materials: {},
            belongTo: BELONG_TYPE.ENEMY_SIDE,
        },
        ["46"]: {
            soldiers: { ["soldier-1"]: 100 },
            materials: {},
            belongTo: BELONG_TYPE.ENEMY_SIDE,
        },
    }; // 自己城池中驻兵（兵属于城池）

    // 战报记录
    private battleReportLog:
        | {
              belong: BELONG_TYPE;
              cityId: string;
              attack: { [soldierId: string]: number };
              defense: { [soldierId: string]: number };
          }[]
        | undefined;

    private duringTime = 0;

    initMapData = false;
    initCityData = false;

    readyBattleCity: { cityId: string; belong: BELONG_TYPE } | undefined;

    protected init() {
        Engine.resManager.loadPaths<JsonAsset>(["cfg/map", "cfg/city"]).then((res: JsonAsset[]) => {
            const mapJson: any = res[0].json;
            if (mapJson && Object.keys(mapJson).length > 0) {
                this.bgBlockList = mapJson.bgTile;
                this.terrainList = mapJson.terrians;
                this.terrainLines = mapJson.lines;
            }
            const cityJson: any = res[1].json;
            if (cityJson && Object.keys(cityJson).length > 0) {
                this.cityCfgDatas = cityJson;
            }
            Engine.eventManager.emit(EVENT_NAME.WORLD_MAP_INITED);
            this._init = true;

            const cityIds = Object.keys(this.terrainList);
            const cityMap: Map<string, number> = new Map();
            for (const line in this.terrainLines) {
                cityMap.set(line, this.terrainLines[line].len);
            }
            this.dijks = new Dijkstra(cityIds, cityMap);
        });
    }

    public initData() {
        const storyCityListStr = gameRecordManager.getPlayerDataByKey(PLAYER_STORAGE_KEY.STORE_CITY_LIST);
        if (storyCityListStr) {
            this.cityList = JSON.parse(storyCityListStr);
        }
        this.intSoldiers();
    }

    // 初始化士兵数到缓存
    intSoldiers() {
        for (const cityId in this.cityList) {
            for (const soldierId in this.cityList[cityId].soldiers) {
                const quantity = this.cityList[cityId].soldiers[soldierId];
                // 玩家士兵信息修改
                const dataPlayer: PlayerData | EnemyData | undefined = this.getPlayerByBelong(
                    this.cityList[cityId].belongTo
                );
                if (dataPlayer) {
                    dataPlayer.changeSoldiers(soldierId, quantity);
                }
            }
        }
    }

    // 根据归属获取玩家信息
    getPlayerByBelong(belong: BELONG_TYPE) {
        if (belong === BELONG_TYPE.SELF_SIDE) {
            return dataManager.dataPlayer;
        } else if (belong === BELONG_TYPE.ENEMY_SIDE) {
            return dataManager.dataEnemy;
        } else {
        }
    }

    // 城池士兵
    public getCitySoldiers(cityId: string) {
        return this.cityList[cityId].soldiers;
    }

    // 更新归属和士兵数量
    public changeCityBelong(cityId: string, belong: BELONG_TYPE) {
        if (!this.cityList[cityId]) {
            const cityDetail = dataManager.dataMap.getCityDetail(cityId)!;
            this.cityList[cityId] = {
                soldiers: cityDetail.armyData,
                materials: cityDetail.materialData,
                belongTo: cityDetail.belong,
            };
        } else {
            // 归属改变移除城市建筑
            const dataPlayer: PlayerData | EnemyData | undefined = this.getPlayerByBelong(
                this.cityList[cityId].belongTo
            );
            if (dataPlayer) {
                dataPlayer.removeBuilding(cityId);
            }
        }
        if (belong !== this.cityList[cityId].belongTo) {
            // 归属改变资源清空
            this.cityList[cityId].soldiers = {};
            this.cityList[cityId].materials = {};
            this.cityList[cityId].belongTo = belong;
            this.cityList[cityId].lastStoreTime = getNowTime();
            gameRecordManager.setPlayerDataByKey(PLAYER_STORAGE_KEY.STORE_CITY_LIST, this.cityList);

            // 城池归属改变
            Engine.eventManager.emit(EVENT_NAME.CHANGE_CITY_BELONG_TYPE, cityId);
        }
    }

    // 修改城池士兵数量
    public changeCitySoldiers(cityId: string, changeSoldiers: { [soldierId: string]: number }) {
        if (!this.cityList[cityId]) {
            const cityDetail = dataManager.dataMap.getCityDetail(cityId)!;
            this.cityList[cityId] = {
                soldiers: cityDetail.armyData,
                materials: cityDetail.materialData,
                belongTo: cityDetail.belong,
            };
        }
        // 玩家士兵信息修改
        const dataPlayer: PlayerData | EnemyData | undefined = this.getPlayerByBelong(this.cityList[cityId].belongTo);
        for (const soldierId in changeSoldiers) {
            const changeValue = changeSoldiers[soldierId];
            const originCityNum = this.cityList[cityId].soldiers[soldierId]
                ? this.cityList[cityId].soldiers[soldierId]
                : 0;
            const currentCityNum = Number(originCityNum) + changeValue;
            if (currentCityNum <= 0) {
                delete this.cityList[cityId].soldiers[soldierId];
            } else {
                this.cityList[cityId].soldiers[soldierId] = currentCityNum;
            }
            if (dataPlayer) {
                dataPlayer.changeSoldiers(soldierId, changeValue);
            }
        }
        this.cityList[cityId].lastStoreTime = getNowTime();
        gameRecordManager.setPlayerDataByKey(PLAYER_STORAGE_KEY.STORE_CITY_LIST, this.cityList);
        Engine.eventManager.emit(EVENT_NAME.UPDATE_CITY, cityId);

        Engine.eventManager.emit(this.buildEventName(DATA_OPTION.UPDATE, DATA_PROPERTY.soldiers));
    }

    // 缺粮造成士兵自杀
    public lackResourceKillSoldiers(belong: BELONG_TYPE, killSoldierIds: string[]) {
        const dataPlayer: PlayerData | EnemyData | undefined = this.getPlayerByBelong(belong);
        for (const cityId in this.cityList) {
            if (this.cityList[cityId].belongTo === belong) {
                killSoldierIds.forEach((soldierId) => {
                    if (this.cityList[cityId].soldiers[soldierId]) {
                        const killNum = getNumFloor(this.cityList[cityId].soldiers[soldierId] * 0.1) + 1;
                        this.cityList[cityId].soldiers[soldierId] = this.cityList[cityId].soldiers[soldierId] - killNum;

                        if (dataPlayer) {
                            dataPlayer.changeSoldiers(soldierId, -killNum);
                        }
                    }
                });
            }
        }

        // Engine.eventManager.emit(this.buildEventName(DATA_OPTION.UPDATE, DATA_PROPERTY.SOLDIER_KILL));
    }

    // 计算自增资源数量
    private calculateCurrentResNum(lastStoreTime: number, proData: any) {
        const passTime = getPassTimeForSecond(lastStoreTime);
        const currentProVal = proData.proVal + Math.floor(passTime / 60) * proData.speed;
        return Math.floor(currentProVal > proData.limit ? proData.limit : currentProVal);
    }

    // 城池数据(配置基本信息 + 玩家对应的信息:驻兵数量)
    public getCityDetail(cityId: string) {
        const terrian = this.terrainList[cityId];
        const terrianType = terrian.type;
        const terrianTypeCfgData = XLSX.TERRAIN["terrian-" + terrianType];
        if (!terrianTypeCfgData) {
            Logger.error("error terrian type:", terrianType);
            return undefined;
        }

        let belongTo = BELONG_TYPE.MIDDLE_SIDE;
        const cityInfoData = this.cityList[cityId];
        const cityOriginData = this.cityCfgDatas[cityId]; // TODO 原始数据没有士兵id和数量，先认为是soldier-1 的兵种数量
        let armyData: { [soldierId: string]: number } = {};
        let materialData: { [materialId: string]: number } = {};

        // 无记录城池初始资源
        if (!cityInfoData) {
            for (const proKey in cityOriginData) {
                if (proKey !== "cityName") {
                    const proData = cityOriginData[proKey];
                    let proVal = proData.proVal;
                    if (proData.speed > 0) {
                        // 数据自增属性,计算当前数量
                        const createTime = dataManager.dataAccount.accountData!.cTime;
                        proVal = this.calculateCurrentResNum(createTime, proData);
                    }
                    // 士兵信息
                    if (proKey.startsWith("soldier-")) {
                        armyData[proKey] = proVal;
                    } else if (proKey.startsWith("material-")) {
                        materialData[proKey] = proVal;
                    }
                }
            }
        } else {
            // 有信息记录的城池
            belongTo = cityInfoData.belongTo;
            armyData = cityInfoData.soldiers;
            materialData = cityInfoData.materials;
        }

        // 城池当前资源
        const terrianDetailData: CityInfoData = {
            id: cityId,
            name: terrianTypeCfgData.name,
            pos: terrian.pos,
            type: terrian.type,
            res: terrianTypeCfgData.res,
            des: terrianTypeCfgData.des,
            requiredBuildTypes: terrianTypeCfgData.buildingIds,
            armyData: armyData,
            materialData: materialData,
            belong: belongTo,
        };
        return terrianDetailData;
    }

    // 获取城池资源的产出和上限
    public getCityOriginData(cityId: string) {
        return this.cityCfgDatas[cityId];
    }

    // 建筑数据
    public getBuildDetail(cityId: string) {
        if (!this.cityList[cityId]) {
            return;
        }
        const dataPlayer: PlayerData | EnemyData | undefined = this.getPlayerByBelong(this.cityList[cityId].belongTo);
        if (!dataPlayer) {
            return;
        }
        const buildData = dataPlayer!.buildList[cityId];
        if (!buildData) {
            return;
        }
        const buildCfgData: Building = this.getBuildCfg(buildData.cfgId)!;
        const buildDetailData: BuildInfoData = {
            id: buildData.id,
            cityId: cityId,
            cfgId: buildData.cfgId,
            name: buildCfgData.name,
            type: buildCfgData.sheetType,
            res: buildCfgData.res,
            des: buildCfgData.des,
            outMaterials: buildCfgData.outMeterials,
        };

        return buildDetailData;
    }

    // 建筑配置
    getBuildCfg(buildCfgId: string) {
        const buildCfg = XLSX.BUILDING[buildCfgId];
        if (!buildCfg) {
            Logger.error("error buildId:", buildCfgId);
            return;
        }
        return buildCfg;
    }

    // 资源配置
    getMaterialCfg(materialCfgId: string) {
        const materialCfg = XLSX.MATERIAL[materialCfgId];
        if (!materialCfg) {
            Logger.error("error materialCfgId:", materialCfgId);
            return;
        }
        return materialCfg;
    }

    // 资源配置
    getSoldierCfg(soldier: string) {
        const soldierCfg = XLSX.SOLDIER[soldier];
        if (!soldierCfg) {
            Logger.error("error soldier:", soldier);
            return;
        }
        return soldierCfg;
    }

    // 建筑列表 , buildType"建造页签
    // 1 生产
    // 2 军事
    // 3 奇迹
    getBuildList(buildType: number | null) {
        if (buildType !== null) {
            const buildList: { [id: string]: Building } = {};
            for (const buildId in XLSX.BUILDING) {
                const buildCfg = XLSX.BUILDING[buildId];
                if (buildType === buildCfg.sheetType) {
                    buildList[buildId] = buildCfg;
                }
            }
            return buildList;
        }
        return XLSX.BUILDING;
    }

    // 部队出征
    dispatchArmy(
        startCityId: string,
        targetCityId: string,
        armyData: { [soldierId: string]: number },
        belong: BELONG_TYPE
    ) {
        // 出发城士兵渐少
        let dispatchArmyNum = 0;
        const soldiers: { [soldierId: string]: number } = {};
        for (const soldierId in armyData) {
            const soldierNum = armyData[soldierId];
            if (soldierNum > 0) {
                soldiers[soldierId] = -soldierNum;
                dispatchArmyNum += soldierNum;
            }
        }
        if (dispatchArmyNum > 0) {
            // 出兵队列
            const startTime = getNowTime();
            const armyId = startTime + "";
            const paths = this.findPath(startCityId, targetCityId, belong);
            const armyInfo: DispatchArmyInfo = {
                armyId: armyId,
                soldiers: armyData,
                fromCityId: startCityId,
                toCityId: targetCityId,
                currentCityId: startCityId,
                nextCityId: paths[1],
                paths: paths,
                startTime: startTime,
                passTime: 0,
                currentCityPassTime: 0,
                belong: belong,
            };

            const dataPlayer: PlayerData | EnemyData = this.getPlayerByBelong(belong)!;
            dataPlayer.addDispatchingArmy(armyInfo.armyId, armyInfo); // 添加到队列
            this.changeCitySoldiers(startCityId, soldiers); // 从城池中移除

            Engine.eventManager.emit(EVENT_NAME.START_DISPATCH_ARMY, armyInfo);
        }
    }

    // 出征部队到达目标，结束移动，进行响应操作
    stopDispatchArmy(armyId: string, startCityId: string, targetCityId: string, belong: BELONG_TYPE) {
        Engine.eventManager.emit(EVENT_NAME.STOP_DISPATCH_ARMY, armyId);

        // 开始战斗结束战斗
        this.endBattle(armyId, startCityId, targetCityId, belong);
    }

    // 结束战斗
    endBattle(armyId: string, startCityId: string, targetCityId: string, belong: BELONG_TYPE) {
        // 计算战斗结果
        const startCityDetail = dataManager.dataMap.getCityDetail(startCityId);
        const targetCityDetail = dataManager.dataMap.getCityDetail(targetCityId);
        const targetCityArmy: { [soldierId: string]: number } = {};
        Object.assign(targetCityArmy, targetCityDetail!.armyData);
        let dataPlayer: PlayerData | EnemyData | undefined;
        if (belong === BELONG_TYPE.SELF_SIDE) {
            dataPlayer = dataManager.dataPlayer;
        } else if (belong === BELONG_TYPE.ENEMY_SIDE) {
            dataPlayer = dataManager.dataEnemy;
        }
        if (dataPlayer === undefined) {
            console.error("error player data");
            return;
        }
        const dispatchingArmy = dataPlayer.dispatchingArmyList[armyId].soldiers;
        dataPlayer.removeDispatchingArmy(armyId); // 移除士兵出征队列

        const attackArmyTypePower = dataManager.dataSoldier.calculateArmyAttackPower(dispatchingArmy);
        const dispatchingArmyTotal = dataManager.dataSoldier.getPowerTotalNum(attackArmyTypePower);

        const defArmyTypePower = dataManager.dataSoldier.calculateArmyDefPower(targetCityArmy, attackArmyTypePower);
        const targetCityTotal = dataManager.dataSoldier.getPowerTotalNum(defArmyTypePower);

        if (targetCityDetail!.belong !== startCityDetail!.belong) {
            // 不同归属战斗
            let isWin = false;
            if (targetCityTotal > dispatchingArmyTotal) {
                // 修改目标城池兵力 （简单相减）
                const winRatioFailPow = targetCityTotal !== 0 ? Math.pow(dispatchingArmyTotal / targetCityTotal, 2) : 0;
                isWin = false;
                Logger.log("winRatioFailPow:", winRatioFailPow);
                const changeArmy: { [soldierId: string]: number } = {};
                for (const soldierId in targetCityArmy) {
                    changeArmy[soldierId] = -Math.floor(Number(targetCityArmy[soldierId]) * winRatioFailPow);
                }
                this.changeCitySoldiers(targetCityId, changeArmy);
            } else {
                // 目标城池变为自己城池,兵力为派遣损失之后的兵力
                isWin = true;
                const winRatioFailPow = targetCityTotal !== 0 ? Math.pow(targetCityTotal / dispatchingArmyTotal, 2) : 0;
                for (const soldierId in dispatchingArmy) {
                    dispatchingArmy[soldierId] -= Math.floor(Number(dispatchingArmy[soldierId]) * winRatioFailPow);
                    if (dispatchingArmy[soldierId] < 0) {
                        Logger.error("why 不应该<0的");
                    }
                }
                // const changeArmy: { [soldierId: string]: number } = {};
                // for (const soldierId in targetCityArmy) {
                //     changeArmy[soldierId] = -Number(targetCityArmy[soldierId]);
                // }
                // for (const soldierId in dispatchingArmy) {
                //     if (changeArmy[soldierId]) {
                //         changeArmy[soldierId] += Number(dispatchingArmy[soldierId]);
                //     } else {
                //         changeArmy[soldierId] = Number(dispatchingArmy[soldierId]);
                //     }
                // }
                this.changeCityBelong(targetCityId, belong);
                this.changeCitySoldiers(targetCityId, dispatchingArmy);
            }

            Engine.eventManager.emit(EVENT_NAME.BATTLE_END, isWin, targetCityId, dispatchingArmyTotal, targetCityTotal);
            // 战报存储
            this.exclStoryBattleReport(belong, targetCityId, dispatchingArmy, targetCityArmy);
        } else {
            // 相同归属任务是派遣，兵力相加
            const soldiers: { [soldierId: string]: number } = {};
            for (const soldierId in dispatchingArmy) {
                const soldierNum = Number(dispatchingArmy[soldierId]);
                soldiers[soldierId] = soldierNum;
            }
            this.changeCitySoldiers(targetCityId, soldiers);
        }
    }

    getCityTotalArmyNum(soldierInfo: { [soldierId: string]: number }) {
        let totalArmyNum = 0;
        for (const soldierId in soldierInfo) {
            totalArmyNum += soldierInfo[soldierId];
        }
        return totalArmyNum;
    }

    // 获取战报数据
    getBattleReportLog() {
        if (!this.battleReportLog) {
            const storyBattleReportListStr = gameRecordManager.getPlayerDataByKey(
                PLAYER_STORAGE_KEY.STORE_BATTLE_REPORT_LIST
            );
            if (storyBattleReportListStr) {
                this.battleReportLog = JSON.parse(storyBattleReportListStr);
            } else {
                this.battleReportLog = [];
            }
        }
        return this.battleReportLog;
    }

    // 战报存储
    exclStoryBattleReport(
        belong: BELONG_TYPE,
        cityId: string,
        attack: { [soldierId: string]: number },
        defense: { [soldierId: string]: number }
    ) {
        const battleReportList = this.getBattleReportLog()!;
        const battleReport = {
            belong: belong,
            cityId: cityId,
            attack: attack,
            defense: defense,
        };
        battleReportList.push(battleReport);
        gameRecordManager.setPlayerDataByKey(PLAYER_STORAGE_KEY.STORE_BATTLE_REPORT_LIST, this.battleReportLog);
    }

    // 城池间连线信息
    getTwoCityLine(oneCityId: string, otherCityId: string) {
        let cityLine = dataManager.dataMap.terrainLines[oneCityId + "-" + otherCityId];
        if (!cityLine) {
            cityLine = dataManager.dataMap.terrainLines[otherCityId + "-" + oneCityId];
        }
        return cityLine;
    }

    // 线路 , 获取当前再线路的位置
    getPathsLen(paths: string[]) {
        let startId = paths[0];
        let totalCityLen = 0;
        for (let index = 0; index < paths.length; index++) {
            if (startId !== paths[index]) {
                const cityId = paths[index];
                const cityLine = dataManager.dataMap.getTwoCityLine(startId, cityId);
                if (!cityLine) {
                    break;
                }
                totalCityLen += cityLine.len;
                startId = cityId;
            }
        }
        return Math.floor(totalCityLen);
    }

    /**
     *  找出最短路径
     * @param startVertex 起始位置
     * @param targetVertex 目标位置
     */
    findPath(startVertex: string, targetVertex: string, side?: BELONG_TYPE) {
        const walkablePaths: string[] = [];
        if (!this.dijks) {
            return walkablePaths;
        }
        const pathVertex: string[] = [targetVertex];
        // 从后向前 遍历顶点 找出路径
        const toVertexPaths = this.dijks.findPath(startVertex);
        let currentVertex = targetVertex;
        while (toVertexPaths[currentVertex]) {
            currentVertex = toVertexPaths[currentVertex];
            pathVertex.push(currentVertex);
        }
        pathVertex.push(startVertex);
        const paths = pathVertex.reverse(); // [...new Set(pathVertex)]

        for (let index = 0; index < paths.length; index++) {
            const cityId = paths[index];
            walkablePaths.push(cityId);
            const belongTo = this.cityList[cityId]?.belongTo || BELONG_TYPE.MIDDLE_SIDE;
            if (belongTo !== side) {
                break;
            }
        }
        console.log("start %s to end %s: %s", startVertex, targetVertex, walkablePaths.toString());
        return walkablePaths;
    }

    // 每秒执行存储
    preFrameUpdate(dt: number) {
        this.duringTime += dt;
        // if (this.duringTime % 10) {
        // }
    }
}
